{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pandas 中的字符串格式化与组合\n",
    "\n",
    "在数据分析和报告中，我们经常需要将不同列的数据组合成新的、具有特定格式的字符串。例如，将“姓”和“名”合并为“全名”，或者根据多个数据点生成一个描述性标签。\n",
    "\n",
    "本教程将详细介绍在 Pandas 中进行字符串格式化的几种常用方法，并澄清一些常见的误区。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 导入库并准备数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "df = pd.DataFrame({\n",
    "    'first_name': ['John', 'Jane', 'Peter', 'Mary'],\n",
    "    'last_name': ['Doe', 'Smith', 'Jones', 'Williams'],\n",
    "    'age': [28, 34, 45, 22],\n",
    "    'city': ['New York', 'London', 'Paris', 'Tokyo']\n",
    "})\n",
    "\n",
    "print(\"原始 DataFrame:\")\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 简单的字符串拼接\n",
    "\n",
    "对于简单的组合，直接使用 `+` 运算符是最直接的方法。你可以将两个字符串类型的 Series 相加，或者将一个 Series 与一个固定的字符串相加。\n",
    "\n",
    "**注意**：进行拼接的所有列都必须是字符串类型。如果包含数字等其他类型，需要先用 `.astype(str)` 进行转换。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 合并 first_name 和 last_name，中间加一个空格\n",
    "df['full_name'] = df['first_name'] + ' ' + df['last_name']\n",
    "\n",
    "# 创建一个描述性字符串，需要将 age 转换为字符串类型\n",
    "df['description'] = df['full_name'] + ' is ' + df['age'].astype(str) + ' years old.'\n",
    "\n",
    "df[['full_name', 'description']]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 使用 `.apply` 和 f-string (推荐方法)\n",
    "\n",
    "当格式化逻辑变得复杂时，使用 `.apply()` 结合 Python 的 f-string 或 `.format()` 方法会更加灵活和清晰。这种方法可以让你在行级别上访问所有列的数据。\n",
    "\n",
    "**澄清**：Pandas 的 `.str` 访问器本身没有 `.format()` 方法。格式化通常是通过 `.apply()` 在每行上调用 Python 的标准字符串格式化功能来实现的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用 .apply 和 f-string 创建一个更复杂的描述\n",
    "# axis=1 表示在行上进行操作\n",
    "df['detailed_info'] = df.apply(\n",
    "    lambda row: f\"{row['full_name']} lives in {row['city']}. Age: {row['age']}.\",\n",
    "    axis=1\n",
    ")\n",
    "\n",
    "df[['detailed_info']]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. `.str.get()` 的正确用法：访问集合元素\n",
    "\n",
    "`.str.get(i)` 并非用于格式化，而是用于从 Series 中每个元素的列表、元组或字典里提取项目。\n",
    "\n",
    "- 如果元素是列表/元组，`i` 是整数索引。\n",
    "- 如果元素是字典，`i` 是键名。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s_list = pd.Series([['a', 'b', 'c'], ['d', 'e'], ['f']])\n",
    "\n",
    "# 获取每个列表的第一个元素\n",
    "print(\"获取列表的第一个元素:\")\n",
    "print(s_list.str.get(0))\n",
    "\n",
    "# 获取第二个元素，对于长度不足的列表，返回 NaN\n",
    "print(\"\\n获取列表的第二个元素:\")\n",
    "print(s_list.str.get(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s_dict = pd.Series([{'name': 'Alice', 'age': 30}, {'name': 'Bob'}, {'age': 25}])\n",
    "\n",
    "# 获取每个字典中 'name' 键对应的值\n",
    "print(\"获取字典中 'name' 的值:\")\n",
    "print(s_dict.str.get('name'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 总结\n",
    "\n",
    "- **简单拼接**: 使用 `+` 运算符，记得用 `.astype(str)` 转换非字符串列。\n",
    "- **复杂格式化**: 使用 `.apply(lambda row: f\"...\", axis=1)`，这是最灵活、最推荐的方式。\n",
    "- **`.str.get()`**: 用于从列表或字典等集合类型的元素中提取数据，而不是用于格式化字符串。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ml311",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
